import LocalStorageManager from "../common/LocalStorageManger";
import UI from "../common/UI";
import Util from "../common/Util";
import { LevelManager, levelManager } from "./LevelManager";
import Piece from "./Piece";
import SettlePanel from "./SettlePanel";

const { ccclass, property } = cc._decorator;

@ccclass
export default class IdiomPuzzleGame extends cc.Component {

    private useTime: number = 0
    private useTimeLabel: cc.Label

    private useStep: number = 0
    private useStepLabel: cc.Label

    private sideCount: number = 4

    public margin: number = 4

    private shuffleTimes: number = 1000

    private labels: string[][] = []
    private levelInfo: any[]
    private curLevel: number = 1

    private pieces: Piece[][] = []

    private blackPiece: Piece = null

    private gameContentNode: cc.Node = null

    @property(cc.Prefab)
    private piecePrefab: cc.Prefab = null

    @property(cc.Prefab)
    public settlePrefab: cc.Prefab = null

    protected onLoad(): void {
        this.gameContentNode = UI.Child(this.node, 'content')
        UI.OnClick(this.node, 'back', function () {
            cc.director.loadScene('main', null)
        })

        this.useTimeLabel = UI.GetComponent(this.node, 'time', cc.Label)
        this.useStepLabel = UI.GetComponent(this.node, 'step/label', cc.Label)

        this.curLevel = LevelManager.curLevel
        this.levelInfo = levelManager.level(this.curLevel)
        UI.Label(this.node, 'title', `第${this.curLevel}关`)
        for (let row = 0; row < this.levelInfo.length; row++) {
            const idiom = this.levelInfo[row];

            for (let col = 0; col < this.sideCount; col++) {
                this.labels[row] = this.labels[row] ? this.labels[row] : []
                this.labels[row][col] = idiom['text'].charAt(col)
            }
        }

        this.init()
    }

    private init() {
        let gameContentHeight: number = this.gameContentNode.height
        for (let row = 0; row < this.sideCount; row++) {
            for (let col = 0; col < this.sideCount; col++) {
                let pieceNode = cc.instantiate(this.piecePrefab)
                pieceNode.setParent(this.gameContentNode)
                let piece: Piece = pieceNode.getComponent(Piece)
                piece.init(row, col, this.labels[row][col])
                piece.node.position = cc.v3(this.margin * (col + 1) + pieceNode.width * col,
                    gameContentHeight - (this.margin * (row + 1) + pieceNode.height * row), 0)

                this.pieces[row] = this.pieces[row] ? this.pieces[row] : []
                this.pieces[row][col] = piece

                // 将最后一个给黑掉
                if (row == this.sideCount - 1 && col == this.sideCount - 2) {
                    piece.black()
                    this.blackPiece = piece
                }

                if (col == this.sideCount - 1) {
                    piece.forbidMove()
                }

                UI.OnClick(pieceNode, null, () => {
                    if (this.move(piece)) {
                        this.useStep++
                        this.useStepLabel.string = this.useStep.toString()
                        if (this.isComplete()) {
                            this.setttle()
                        }
                    }
                }, this)
            }
        }

        this.shuffle()
    }

    private shuffle() {
        // 打乱
        for (let times = 0; times < this.shuffleTimes; times++) {
            this.exchangePiece()
        }

        while (!this.canComplete()) {
            this.exchangePiece()
        }
    }

    private canComplete() {
        // 验证是否是奇排列，如果是奇排列则需要进行对换，使其变成偶排列，不然会出现无法还原的情况
        let reversedCount = 0   // 逆序(前面的数大于后面的数)数，逆序数为偶数则数列为偶排序，逆序数为奇数则为奇排列
        for (let curIdx = 0; curIdx < this.sideCount ** 2; curIdx++) {
            let row = Math.floor(curIdx / this.sideCount)
            let col = Math.floor(curIdx % this.sideCount)
            let curPiece = this.pieces[row][col]
            for (let nextIdx = curIdx + 1; nextIdx < this.sideCount ** 2; nextIdx++) {
                let nextPieceRow = Math.floor(nextIdx / this.sideCount)
                if (nextPieceRow >= this.sideCount) {
                    break
                }

                let nextPieceCol = Math.floor(nextIdx % this.sideCount)
                let nextPiece = this.pieces[nextPieceRow][nextPieceCol]
                if (curPiece.orderIdx(this.sideCount) - nextPiece.orderIdx(this.sideCount) > 0) {
                    reversedCount++
                }
            }
        }

        // 因为只有黑piece才能进行移动，所以要加上黑piece的行和列
        let resultCount = reversedCount + this.blackPiece.row + this.blackPiece.col + 1
        return resultCount % 2 == 0
    }

    private isComplete() {
        for (let row = 0; row < this.sideCount; row++) {
            for (let col = 0; col < this.sideCount; col++) {
                let piece = this.pieces[row][col]
                if (piece.row != piece.trueRow || piece.col != piece.trueCol) {
                    return false
                }
            }
        }

        return true
    }

    private move(piece: Piece) {
        let row = piece.row
        let col = piece.col;

        if (!piece.canExchange(this.blackPiece) || !piece.canMove) {
            return false
        }

        [this.pieces[row][col], this.pieces[this.blackPiece.row][this.blackPiece.col]] =
            [this.pieces[this.blackPiece.row][this.blackPiece.col], this.pieces[row][col]]
        return piece.exchange(this.blackPiece)
    }

    private exchangePiece() {
        let row = Util.randomInt(0, this.sideCount)
        let col = Util.randomInt(0, this.sideCount)

        let piece = this.pieces[row][col];
        if (!piece.canMove) {
            return
        }

        [this.pieces[row][col], this.pieces[this.blackPiece.row][this.blackPiece.col]] =
            [this.pieces[this.blackPiece.row][this.blackPiece.col], this.pieces[row][col]]
        piece.exchange(this.blackPiece, false);
    }

    protected update(dt: number): void {
        this.useTime += dt
        let minute = Math.trunc(this.useTime / 60)
        let second = Math.trunc(this.useTime % 60)
        this.useTimeLabel.string = `${minute < 10 ? '0' + minute : minute}:${second < 10 ? '0' + second : second}`
    }

    async setttle() {
        let settlePanel = cc.instantiate(this.settlePrefab)
        settlePanel.setParent(this.node)
        let curMaxLevel = LocalStorageManager.get('cur_level', this.curLevel)
        if (this.curLevel >= curMaxLevel) {
            LocalStorageManager.set('cur_level', this.curLevel + 1)
        }

        settlePanel.getComponent(SettlePanel).init(this.levelInfo)
    }
}
